package video.com.service.impl;



import com.Exception.GlobalException;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.constant.ResponseDataConstant;
import com.contant.RedisConstantPrefix;
import com.feign.CommonFeignClient;
import com.google.gson.Gson;
import com.responseData.R;
import com.responseData.ResponseData;
import com.utils.CommonBeanUtil;
import com.utils.GenerateNum;
import lombok.extern.slf4j.Slf4j;


import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import video.com.activeMQ.MqConstants;
import video.com.activeMQ.product.Product;
import video.com.aspect.FormatFileURL;
import video.com.entity.UserBehavior;
import video.com.entity.Video;
import video.com.entity.VideoFile;
import video.com.mapper.UserBehaviorMapper;
import video.com.recommend.Recommend;
import video.com.service.TagService;
import video.com.transition.VideoTransaction;
import video.com.transportEntity.vo.Page;
import video.com.mapper.VideoMapper;
import video.com.service.VideoApproveService;
import video.com.service.VideoFileService;
import video.com.service.VideoService;
import video.com.transportEntity.vo.VideoListPage;
import video.com.utils.ConvertDateUtils;
import video.com.utils.IdCreator;
import video.com.utils.RedisUtils;
import video.com.utils.UserInfoUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xwj
 * @since 2023-11-11
 */
@Slf4j
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {

    @Autowired
    private CommonFeignClient feignClient;

    @Autowired
    private TagService tagService;

    @Autowired
    private Product product;

    @Autowired
    private VideoFileService videoFileService;

    @Resource
    private VideoMapper videoMapper;

    @Resource
    private UserBehaviorMapper userBehaviorMapper;

    @Autowired
    private VideoTransaction videoTransaction;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private Recommend recommend;

    /**
     * 若有延时发布则需要将信息、以及审核信息利用消息队列延迟发布
     * @param info
     * @return
     */
    @Override
    public Object uploadVideoInfo(Map<String, Object> info) throws Exception{

        boolean flag = false;
        long delayTimePublish = 0;
        String videoId = GenerateNum.generateOrder();
        try {

            Video video = CommonBeanUtil.getInstance().mapToObject(info, Video.class);

            //拿到redis中保存的视频文件地址实体 视频封面地址实体
            Object file = RedisUtils.redis.opsForValue().get(RedisConstantPrefix.VIDEO_FILE_PREFIX + video.getVideoFileId());
            if(file == null){
                throw new GlobalException(ResponseData.error("视频信息间隔时间过长"));
            }
            VideoFile videoFile = new Gson().fromJson(file.toString(),VideoFile.class);

            Object cover = RedisUtils.redis.opsForValue().get(RedisConstantPrefix.VIDEO_COVER_PREFIX + video.getVideoCover());
            if(cover == null){
                throw new GlobalException(ResponseData.error("视频信息间隔时间过长"));
            }
            VideoFile videoFileCover = new Gson().fromJson(cover.toString(),VideoFile.class);

            video.setUploadTime(new Date());

            R userInfoById = feignClient.getUserInfoById(video.getUploadUserId());

            //TODO 还应该与当前登录的用户信息对比，应写入拦截器中 以此校验
            if(userInfoById.getCode() != ResponseDataConstant.SUCCESS){
                log.info("非法的上传行为,未获取到上传者的用户信息");
                throw new GlobalException(ResponseData.unusualCustomize(ResponseDataConstant.NO_PERMISSION,"非法的上传行为"));
            }

            video.setVideoId(videoId);

            List<String> tags = (List<String>) info.get("tags");
            //所有储存动作完成后才确定是否在审核列表里放入数据
            delayTimePublish = ConvertDateUtils.calTimeFromNow(info.get("delayTime").toString());

            videoTransaction.execute(() -> {
                this.save(video);
                videoFileService.insertBatch(videoFileCover,videoFile);
                tagService.batchInsertIntoVideoTag(tags,videoId);
            });

            flag = true;

        }catch (Exception e){
            e.printStackTrace();
            if(!(e instanceof GlobalException)){
                log.info("请求失败,[{}]",e.getMessage());
            }
            flag = false;
            throw e;

        }finally{
            if(flag && delayTimePublish > 0){
                product.delaySendMsg(MqConstants.PUBLISH_TOPIC,videoId,delayTimePublish * 60 * 1000L);
            }else if(!flag){ // 出现异常 将上传的文件与封面删除
                videoFileService.removeBatch(info.get("videoCover").toString(),info.get("videoFileId").toString());
            }
        }


        return flag;
    }

    /**
     * 根据uid 获取视频信息
     * @param uid
     * @return
     * @throws Exception
     */
    @Override
    @FormatFileURL(value = {"cover_url","video_url","avatar"})
    public Map<String, Object> getVideoInfo(String uid) throws Exception {

        Map<String, Object> videoInfo = videoMapper.getVideoInfo(uid);
         
        return videoInfo;
    }

    @Override
    @FormatFileURL(value = {"cover"})
    public List<Map<String, Object>> userRecommend(VideoListPage page) throws Exception {

        List<Map<String, Object>> videoList = new ArrayList<>(20);

        if(page.getUserId() == null || page.getUserId() == 0){
            videoList.addAll(videoMapper.getTopNVideoList(page, 20));
        }else{
            List<Long> longs = recommend.userRecommend(page.getUserId(), page.getNotIncludeIds());
            if(longs.size() > 0){
                videoList.addAll(videoMapper.getVideoListByIds(longs));
            }

            if(longs.size() < 20){
                videoList.addAll(videoMapper.getTopNVideoList(page, 20 - longs.size()));
            }
        }
        //TODO 移植到ES 实现搜索
//        if (UserInfoUtils.getUserInfo() == null) {
//            return videoList;
//        }
        return videoList;
    }

    @Override
    @FormatFileURL(value = {"cover"})
    public List<Map<String, Object>> videoRecommend(VideoListPage page) throws Exception {
        List<Map<String, Object>> videoList = new ArrayList<>(20);
        //首先排除自身
        page.getNotIncludeIds().add(page.getVideoId());

        if(page.getUserId() == null || page.getVideoId() == null){
            videoList.addAll(videoMapper.getTopNVideoList(page,20));
        }else{
            List<Long> longs = recommend.itemRecommend(page.getUserId(), page.getVideoId());

            if(longs.size() > 0){
                videoList.addAll(videoMapper.getVideoListByIds(longs));
            }
            if(longs.size() < 20){

                videoList.addAll(videoMapper.getTopNVideoList(page, 20 - longs.size()));
            }
        }

        return videoList;
    }

    @Override
    public void updateVideoInfoWithUserId(Map<String, Object> info) throws Exception {

        if(info.get("videoId") == null){
            return ;
        }
        String videoId = info.get("videoId").toString();
        info.put("keyId",videoId);

        String userId = UserInfoUtils.getUserInfo().get("userId").toString();
        info.put("userId",userId);

        info.put("behaviorTime",new Date());
        RLock lock = redissonClient.getLock(videoId + userId);
        boolean lockDown = lock.tryLock();
        try{
            if(!lockDown){
                return;
            }

            videoTransaction.execute(() -> {
                UserBehavior userBehavior = userBehaviorMapper.selectByKeyIdAndUSerId(videoId, userId);

                if(userBehavior == null){
                    userBehavior = new UserBehavior();
                    userBehavior.setField(info);
                    userBehavior.setBehaviorTime(new Date());
                    userBehavior.setBehaviorType(1);
                    userBehaviorMapper.insert(userBehavior);
                }else{
                    userBehaviorMapper.updateUserBehavior(info);
                }

                videoMapper.updateVideoInfo(info);
            });

        }finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public void tourist(Map<String, Object> info) throws Exception {
        videoMapper.updateVideoInfo(info);
    }

    @Override
    public Map<String, Object> userBehaviorByVid(String vid) throws Exception {

        Map<String, Object> userInfo = UserInfoUtils.getUserInfo();

        if(userInfo == null){
            throw new GlobalException(ResponseData.unusualCustomize(ResponseDataConstant.USER_USUAL,"用户信息获取错误"));
        }

        String uid = userInfo.get("userId").toString();


        return userBehaviorMapper.getUserActionByUidAndVid(uid, vid);
    }

}
